package sym.qrcode.image;

import java.awt.Color;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;

import sym.qrcode.encoder.Mosaic;

public class ImageMosaicMatrix {
    public final static int WHITETHRESHOLD = 200;
    private int x;
    private int y;
    private int height;
    private int width;
    private Mosaic matrix[][];
    private int scale;
    private BufferedImage resizedImage = null;

    public ImageMosaicMatrix(int x, int y, int height, int width, int scale,
            String fileName) {
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
        this.scale = scale;
        matrix = new Mosaic[height][width];
        BufferedImage image = null;
        try {
            image = ImageIO.read(new File(fileName));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        buildMatrix(image);
    }
    public ImageMosaicMatrix(int x, int y, int height, int width, int scale,
            File file) {
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
        this.scale = scale;
        matrix = new Mosaic[height][width];
        BufferedImage image = null;
        try {
            image = ImageIO.read(file);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        buildMatrix(image);
    }

    public ImageMosaicMatrix(int x, int y, int height, int width, int scale,
            BufferedImage image) {
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
        this.scale = scale;
        matrix = new Mosaic[height][width];
        buildMatrix(image);
    }

    private boolean isWhite(Color color) {
        // TODO Auto-generated method stub
        if (color.getRed() > WHITETHRESHOLD && color.getBlue() > WHITETHRESHOLD
                && color.getGreen() > WHITETHRESHOLD)
            return true;
        return false;
    }

    private void buildMatrix(BufferedImage image) {
        // TODO Auto-generated method stub
        if (image == null)
            return;
        resizedImage = new BufferedImage(width * scale, height * scale,
                BufferedImage.TYPE_INT_RGB);
        resizedImage.getGraphics().drawImage(
                image.getScaledInstance(width * scale, height * scale,
                        Image.SCALE_SMOOTH), 0, 0, null);
        int imageWidth = resizedImage.getWidth();
        int imageHeight = resizedImage.getHeight();

        //System.out.println("w:"+imageWidth+ "  h:"+imageHeight);
        ArrayList<Point> list = new ArrayList<Point>();

        for (int i = 0; i < imageHeight-scale; i += scale) {
            for (int j = 0; j < imageWidth-scale; j += scale) {
                boolean isWhiteBlock = true;
                out: for (int k = 0; k < scale; k++) {
                    for (int l = 0; l < scale; l++) {
                        if (!isWhite(new Color(resizedImage.getRGB(j + k, i + l)))) {
                            isWhiteBlock = false;
                            break out;
                        }
                    }
                }
                if (!isWhiteBlock)
                    break;
                else
                    list.add(new Point(j, i));
            }

            for (int j = imageWidth - scale; j >= 0; j -= scale) {
                boolean isWhiteBlock = true;
                out: for (int k = 0; k < scale; k++) {
                    for (int l = 0; l < scale; l++) {
                        if (!isWhite(new Color(resizedImage.getRGB(j + k, i + l)))) {
                            isWhiteBlock = false;
                            break out;
                        }
                    }
                }
                if (!isWhiteBlock)
                    break;
                else {
                    boolean isInList = false;
                    for (Point p : list) {
                        if (j == p.x && i == p.y) {
                            isInList = true;
                            break;
                        }
                    }
                    if (!isInList)
                        list.add(new Point(j, i));
                }
            }
        }
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                boolean isInList = false;
                for (Point p : list) {
                    if (p.x == i * scale && p.y == j * scale) {
                        isInList = true;
                        break;
                    }
                }
                if (isInList) {
                    Mosaic m = new Mosaic();
                    m.type = Mosaic.TRANSPARENT;
                    matrix[j][i] = m;
                } else {
                    Mosaic m = new Mosaic();
                    m.type = Mosaic.IMAGE;
                    matrix[j][i] = m;
                }
            }
        }
    }

    public Mosaic[][] getMatrix() {
        return matrix;
    }

    public void setMatrix(Mosaic[][] matrix) {
        this.matrix = matrix;
    }

    public int getColorRGB(int x, int y) {
        if (resizedImage == null)
            return -1;
        if (x > resizedImage.getWidth() || y > resizedImage.getHeight())
            return -1;
        return resizedImage.getRGB(x, y);
    }

    public BufferedImage getPartImage(int x, int y, int width, int height) {
        BufferedImage resultImage = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        for (int i = x; i < x + width; i++) {
            for (int j = y; j < y + height; j++) {
                resultImage.setRGB(i - x, j - y, this.getColorRGB(i, j));
            }
        }
        return resultImage;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getScale() {
        return scale;
    }

    public void setScale(int scale) {
        this.scale = scale;
    }
}
